<?php
	class GP_jdb
	{
		public $db;
		public $table;
		public $dir;
		public $ids = array();
        static $_instance;
        
        // singleton
		public function &getInstance($db, $table) 
		{
			if (!self :: $_instance instanceof self)
				self :: $_instance = new self($db, $table);
			return self :: $_instance;
		}
		
		function __construct($db, $table)
		{
			$this->dir = DIR_JDB;
			if (!is_dir($this->dir . $db))
				mkdir($this->dir . $db, 0777);
			$this->db = $this->dir . $db;
			if (!is_dir($this->dir . $db . DIRECTORY_SEPARATOR . $table))
				mkdir($this->dir . $db . DIRECTORY_SEPARATOR . $table, 0777);
			$this->table = $this->dir . $db . DIRECTORY_SEPARATOR . $table;
			$this->ids = $this->ids();
		}
		
		function insert($data)
		{
            $md5 = md5(serialize($data));
			if (!is_file($this->table . DIRECTORY_SEPARATOR . $md5 . '.jdb')) {
				$data['dateCreate'] = time();
                $data['dateUpdate'] = time();
                $jdb = json_encode($data);
				file_put_contents($this->table . DIRECTORY_SEPARATOR . $md5 . '.jdb', $jdb);
			}
			return $md5;
		}
		
		function ids($dir = '')
		{
			$ids = array();
            if (empty($dir)) {
                $dir = glob($this->table . DIRECTORY_SEPARATOR . '*');
            }
            foreach ($dir as $item) {
				if(!is_dir($item)) {
					$exttab = explode('.', $item);
					$extension = strtolower(end($exttab));
					if ($extension == 'jdb') {
						$tabTmp = explode(DIRECTORY_SEPARATOR, $item);
						$id = _r('.' . $extension, '', end($tabTmp));
						if (!in_array($id, $ids)) {
							$ids[] = $id;
                        }
					}
				} else {
					$ids = array_merge($ids, $this->ids($item));
                }
			}
			return $ids;
		}
		
		function get($id, $array = false)
		{
			if (is_file($this->table . '/' . $id . '.jdb')) {
				$ct = file_get_contents($this->table . DIRECTORY_SEPARATOR . $id . '.jdb');
				return json_decode($ct, $array);
			}
			return null;
		}
		
		function update($id, $data)
		{
			$old = get_object_vars($this->get($id));
			$new = array();
			$doublons = array();
			
			if (count($old) > count($data)) {
				foreach ($old as $k => $v) {
					$doublons[] = $k;
					if (!isset($data[$k]))
						$new[$k] = $v;
					else
						$new[$k] = $data[$k];
				}
			} else if (count($old) <= count($data)) {
				foreach ($old as $k => $v) {
					$doublons[] = $k;
					if (!isset($data[$k]))
						$new[$k] = $v;
					else
						$new[$k] = $data[$k];
				}
			}
			
			foreach ($data as $k => $v) {
				if (!in_array($k, $doublons)) {
					$doublons[] = $k;
                    if ($k == 'dateUpdate')
                        $data[$k] = time();
					$new[$k] = $data[$k];
				}
			}
			$this->delete($id);
			return $this->insert($new);
		}
		
		function delete($id)
		{
			if (is_file($this->table . DIRECTORY_SEPARATOR . $id . '.jdb')) {
				unlink($this->table . DIRECTORY_SEPARATOR . $id . '.jdb');
				return true;
			}
			return false;
		}
		
		function search($where = null)
		{
			$return = array();
			$this->ids();
			if (null === $where)
				return $this->ids;
            if (!strstr($where, ' && ') && !strstr($where, ' || ')) {
                list($field, $op, $value) = explode(' ', $where);
                $tmp = array();
                foreach ($this->ids as $id) {
					$obj = $this->get($id);
					if (true === $this->analyze($obj, $field, $op, $value))
						$tmp[] = $id; 
				}
                
                return (count($tmp)) ? $tmp : null;
            }
			
			$newQuery = $where;
			$tabAnd = explode(' && ', $where);
			
			foreach ($tabAnd as $subQuery)
				$newQuery = _r($subQuery . ' && ', '', $newQuery);
			
			$tabOr = explode(' || ', $newQuery);
			
			if (count($tabAnd) > 1) {
				$resAnd = array();
				foreach ($tabAnd as $subQuery) {
					list($field, $op, $value) = explode(' ', $subQuery);
					$tmp = array();
					foreach ($this->ids as $id) {
						$obj = $this->get($id);
						if (true === $this->analyze($obj, $field, $op, $value))
							$tmp[] = $id; 
					}
					if (count($resAnd)) {
						$intersection = array_intersect($resAnd, $tmp);
						$resAnd = $intersection;
					} else
						$resAnd = $tmp;
				}
				$return = array_merge($return, $resAnd);
			}
			
			if (count($tabOr) > 1) {
				$resOr = array();
				foreach ($tabOr as $subQuery) {
					list($field, $op, $value) = explode(' ', $subQuery);
					$tmp = array();
					foreach ($this->ids as $id) {
						$obj = $this->get($id);
						if (true === $this->analyze($obj, $field, $op, $value))
							$tmp[] = $id; 
					}
					$resOr = array_merge($resOr, $tmp);
				}
				$return = array_merge($return, $resOr);
			}
			
			if (count($return))
				return array_unique($return);
			
			return null;
		}
		
		function analyze($obj, $field, $op, $value)
		{
            $comp = $obj->$field;
			if (isset($comp)) {
				$comp = strtolower($comp);
				$value = strtolower($value);
				switch ($op) {
					case '=':
						if ($comp == $value)
							return true;
						break;
					case '>=':
						if ($comp >= $value)
							return true;
						break;
					case '<=':
						if ($comp <= $value)
							return true;
						break;
					case '<>':
					case '!=':
						if ($comp <> $value)
							return true;
						break;
					case 'LIKE':
						if (strstr($comp, $value))
							return true;
						break;
					case 'IN':
						$value = _r('(', '', $value);
						$value = _r(')', '', $value);
						$tabValues = explode(',', $value);
						if (in_array($comp, $tabValues))
							return true;
						break;
					case 'NOT IN':
						$value = _r('(', '', $value);
						$value = _r(')', '', $value);
						$tabValues = explode(',', $value);
						if (!in_array($comp, $tabValues))
							return true;
						break;
				}
			}
			return false;
		}
		
		function limit($array, $length = 10, $start = 0)
		{
			$max = count($array);
			if ($length > $max)
				$length = $max;
			return array_slice($array, $start, $length);
		}
		
		function order($array, $field, $ord = 'ASC')
		{
			$tmp = array();
			$ids = array();
			if (count($array)) {
				foreach ($array as $id) {
					$tab = $this->get($id);
					$ids[] = $id;
					$tmp[] = $tab->$field;
				}
				natcasesort($tmp);
				if ($ord == 'DESC')
					$tmp = array_reverse($tmp);
					
				$return = array();
				foreach ($tmp as $k => $v)
					$return[] = $ids[$k];
				return $return;
			}
			return null;
		}
		
		function array_orderby()
		{
			$args = func_get_args();
			$data = array_shift($args);
			foreach ($args as $n => $field) {
				if (is_string($field)) {
					$tmp = array();
					foreach ($data as $key => $row)
						$tmp[$key] = $row[$field];
					$args[$n] = $tmp;
				}
			}
			$args[] = &$data;
			call_user_func_array('array_multisort', $args);
			return array_pop($args);
		}
		
		function fetchArray($array, $one = false)
		{
			if (count($array)) {
				$return = array();
				foreach ($array as $id)
					$return[] = $this->get($id, true);
				if (true === $one)
					return $return[0];
				return $return;
			}
			return null;
		}
		
		function now()
		{
			return date("Y-d-m H:i:s");
		}
		
		function a2o($array = array()) 
		{
			if (is_array($array)) {
				$data = new stdClass;
				if (!count($array))
					return $data;

				foreach ($array as $aKey => $aVal)
					$data->{$aKey} = $aVal;
				return $data;
			}
			return false;
		}
	}
	// define('DIR_JDB', 'db/');
	// $db = new GP_jdb('test', 'personnes');
	// $db->insert(array('prenom' => 'Gerald', 'nom' => 'Plusquellec'));
	// $id = $db->update($db->ids[0], array('nom' => 'test', 'merci' => 'noki'));
	// $res = $db->fetchArray($db->order($db->search('nom LIKE p || merci LIKE o'), 'nom', 'ASC'));
	// var_dump($res);
?>